package com.tool4j.graph;

import cn.hutool.core.util.StrUtil;
import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import com.alibaba.cloud.ai.graph.streaming.StreamingOutput;
import com.alibaba.fastjson.JSON;
import com.tool4j.entity.GenerateCodeLog;
import com.tool4j.entity.params.ChatParams;
import com.tool4j.graph.factory.GenerateCodeGraphFactory;
import com.tool4j.service.GenerateCodeLogService;
import com.tool4j.sse.SseEventPublisher;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Component
@Slf4j
public class GenerateCodeGraph {

    private final CompiledGraph generateCodeGraph;

    @Autowired
    private SseEventPublisher eventPublisher;

    @Autowired
    private GenerateCodeLogService generateCodeLogService;

    private final ExecutorService executor = Executors.newSingleThreadExecutor();

    public GenerateCodeGraph(ChatClient.Builder chatClientBuilder) throws Exception {
        ChatClient chatClient = chatClientBuilder.build();
        this.generateCodeGraph = GenerateCodeGraphFactory.build(chatClient);
    }

    public void generate(ChatParams params) {
        RunnableConfig runnableConfig = RunnableConfig.builder().threadId(params.getChatId()).build();
        Map<String, Object> objectMap = new HashMap<>();
        String input = params.getContent();
        if (StrUtil.isNotBlank(params.getFileContent())) {
            input = String.format("%s\n\n 附带文件内容：\n%s", params.getContent(), params.getFileContent());
        }
        objectMap.put("input", input);
        objectMap.put("config", params.getToolParams().get("javaCodeConfig"));
        executor.submit(() -> {
            try {
                generateCodeGraph.stream(objectMap, runnableConfig).forEachAsync(output -> {
                    String result = "";
                    if (output instanceof StreamingOutput streamingOutput) {
                        //流式节点输出
                        result = streamingOutput.chunk();
                        String resultMessage = JSON.toJSONString(Map.of("chatId", params.getChatId(), "content", result));
                        eventPublisher.sendMessageToClient(params.getClientId(), resultMessage);
                    } else {
                        String node = output.node();
                        Map<String, Object> data = output.state().data();
                        String resultMessage = null;
                        switch (node) {
                            case "intent":
                                resultMessage = JSON.toJSONString(Map.of("chatId", params.getChatId(), "content", "已接收到用户需求，正在进行分析..."));
                                eventPublisher.sendMessageToClient(params.getClientId(), resultMessage);
                                if (data.get("intent_type") == null) {
                                    break;
                                }
                                String intentType = data.get("intent_type").toString();
                                String message;
                                Map<String, Object> config = (HashMap) data.get("config");
                                if (intentType.contains("sql")) {
                                    message = String.format("""
                                            检测到您输入的内容为 SQL 语句，系统将基于该语句自动生成对应的 Java 增删改查（CRUD）代码：
                                            
                                            - **解析 SQL：** 自动提取表名、字段信息及数据类型等元数据
                                            - **加载模板：** 根据选择的ORM框架加载预设的代码模板：%s
                                            - **配置注入：** 注入自定义配置：
                                              - 包名：%s
                                              - 作者：%s
                                              - 接口前缀：%s
                                              - 忽略表前缀：%s
                                              - 使用Lombok：%s
                                              - 使用Swagger：%s
                                              - 开启类注释：%s
                                              - 开启方法注释：%s
                                              - 开启字段注释：%s
                                            - **模板渲染：** 将解析结果与配置注入模板，生成结构化代码
                                            - **完成输出：** 整合生成结果，提供可直接使用的 Java 代码
                                            
                                            代码正在生成中……""", config.get("ormType"), config.get("basePackage"), config.get("author"), config.get("apiPathPrefix"), config.get("tableNamePrefix"), config.get("useLombok"), config.get("swaggerSupport"), config.get("useClassComment"), config.get("useMethodComment"), config.get("useFieldComment"));
                                } else {
                                    message = String.format("""
                                            检测到您输入的内容为 原始需求，系统将根据需求生成对应的建表语句，基于该语句自动生成对应的 Java 增删改查（CRUD）代码：
                                            - **生成 SQL：** 根据需求生成对应的建表语句
                                            - **解析 SQL：** 自动提取表名、字段信息及数据类型等元数据
                                            - **加载模板：** 根据选择的ORM框架加载预设的代码模板：%s
                                            - **配置注入：** 注入自定义配置：
                                              - 包名：%s
                                              - 作者：%s
                                              - 接口前缀：%s
                                              - 忽略表前缀：%s
                                              - 使用Lombok：%s
                                              - 使用Swagger：%s
                                              - 开启类注释：%s
                                              - 开启方法注释：%s
                                              - 开启字段注释：%s
                                            - **模板渲染：** 将解析结果与配置注入模板，生成结构化代码
                                            - **完成输出：** 整合生成结果，提供可直接使用的 Java 代码
                                            
                                            代码正在生成中……""", config.get("ormType"), config.get("basePackage"), config.get("author"), config.get("apiPathPrefix"), config.get("tableNamePrefix"), config.get("useLombok"), config.get("swaggerSupport"), config.get("useClassComment"), config.get("useMethodComment"), config.get("useFieldComment"));
                                }
                                resultMessage = JSON.toJSONString(Map.of("chatId", params.getChatId(), "content", message));
                                eventPublisher.sendMessageToClient(params.getClientId(), resultMessage);
                                break;
                            case "extract":
                                resultMessage = JSON.toJSONString(Map.of("chatId", params.getChatId(), "content", "\n\n表字段解析完成，开始生成模板代码...\n"));
                                eventPublisher.sendMessageToClient(params.getClientId(), resultMessage);
                                break;
                            case "generateCode":
                                resultMessage = JSON.toJSONString(Map.of("chatId", params.getChatId(), "content", "代码生成成功！\n"));
                                eventPublisher.sendMessageToClient(params.getClientId(), resultMessage);
                                break;
                        }
                        if (data.get("tool_result") != null) {
                            result = data.get("tool_result").toString();
                            resultMessage = JSON.toJSONString(Map.of("chatId", params.getChatId(), "file", result));
                            eventPublisher.sendMessageToClient(params.getClientId(), resultMessage);
                            eventPublisher.sendComplete(params.getClientId());

                            if (StateGraph.END.equals(node)) {
                                try {
                                    GenerateCodeLog generateCodeLog = new GenerateCodeLog();
                                    generateCodeLog.setUserId(params.getUserId());
                                    generateCodeLog.setInput(data.get("input").toString());
                                    generateCodeLog.setOutput(result);
                                    generateCodeLogService.insert(generateCodeLog);
                                } catch (Exception e) {
                                    log.error("保存代码生成记录失败");
                                }
                            }
                        }
                    }
                });
            } catch (GraphRunnerException e) {
                throw new RuntimeException(e);
            }
        });
    }
}
